//
//  HACPainter.m
//  HStockCharts
//
//  Created by Hotacool on 2018/9/14.
//

#import "HACPainter.h"
#import "HACPainterDisplay.h"
#import <objc/runtime.h>

static const char *HACPainterDefaultDelegateKey = "HACPainterDelegateKey";

@implementation HACPainter
+ (instancetype)painterLayer {
    NSLog(@"painterLayer: %@", NSStringFromClass(self));
    Class cls = NSClassFromString(NSStringFromClass(self));
    HACPainterDisplay *paintDisplay = [[HACPainterDisplay alloc] init];
    id instance = [[cls alloc] initWithDelegate:paintDisplay];
    objc_setAssociatedObject(instance, HACPainterDefaultDelegateKey, paintDisplay, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    return instance;
}

- (instancetype)initWithDelegate:(id<HACPainterDelegate>)delegate {
    if (self = [self init]) {
        self.painterDelegate = delegate;
        self.displaysAsynchronously = YES;
    }
    return self;
}

- (void)setPainterDelegate:(id<HACPainterDelegate>)painterDelegate {
    _painterDelegate = painterDelegate;
    // retain painter delegate.
    id<HACPainterDelegate> defaultDelegate = objc_getAssociatedObject(self, HACPainterDefaultDelegateKey);
    if (defaultDelegate) {
        objc_removeAssociatedObjects(defaultDelegate);
    }
}

- (void)display {
    if (self.painterDelegate) {
        [self.painterDelegate displayLayer:self asynchronously:self.displaysAsynchronously];
    } else {
        [super display];
    }
}

- (void)supperDisplay {
    [super display];
}
@end

@interface HACPathPainter ()
@property (nonatomic, strong) NSMutableArray<UIBezierPath *> *innerSubPaths;
@end

@implementation HACPathPainter
+ (instancetype)painterLayer {
    HACPainterDisplay *paintDisplay = [[HACPainterDisplay alloc] init];
    HACPathPainter *instance = [[HACPathPainter alloc] initWithDelegate:paintDisplay];
    objc_setAssociatedObject(instance, HACPainterDefaultDelegateKey, paintDisplay, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    return instance;
}

- (instancetype)initWithDelegate:(id<HACPainterDelegate>)delegate {
    if (self = [self init]) {
        self.painterDelegate = delegate;
        self.displaysAsynchronously = YES;
        self.generatePathAsynchronously = YES;
    }
    return self;
}

- (void)setPainterDelegate:(id<HACPainterDelegate>)painterDelegate {
    _painterDelegate = painterDelegate;
    // retain painter delegate.
    id<HACPainterDelegate> defaultDelegate = objc_getAssociatedObject(self, HACPainterDefaultDelegateKey);
    if (defaultDelegate) {
        objc_removeAssociatedObjects(defaultDelegate);
    }
}

- (void)display {
    if (self.generatePathAsynchronously) {
        dispatch_async([HACPainterDisplay serialOperationQueue], ^{
            UIBezierPath *path = [UIBezierPath bezierPath];
            [self.innerSubPaths enumerateObjectsUsingBlock:^(UIBezierPath * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                [path appendPath:obj];
            }];
            dispatch_async(dispatch_get_main_queue(), ^{
                self.path = path.CGPath;
                if (self.painterDelegate) {
                    [self.painterDelegate displayLayer:self asynchronously:self.displaysAsynchronously];
                } else {
                    [super display];
                }
            });
        });
    } else {
        dispatch_async(dispatch_get_main_queue(), ^{
            UIBezierPath *path = [UIBezierPath bezierPath];
            [self.innerSubPaths enumerateObjectsUsingBlock:^(UIBezierPath * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                [path appendPath:obj];
            }];
            self.path = path.CGPath;
            if (self.painterDelegate) {
                [self.painterDelegate displayLayer:self asynchronously:self.displaysAsynchronously];
            } else {
                [super display];
            }
        });

    }
}

- (void)supperDisplay {
    [super display];
}

- (void)generateSubPath:(GeneratePathBlock)block {
    if (block) {
        if (self.generatePathAsynchronously) {
            dispatch_async([HACPainterDisplay serialOperationQueue], ^{
                UIBezierPath *subPath = block();
                if (subPath) {
                    [self.innerSubPaths addObject:subPath];
                }
            });
        } else {
            UIBezierPath *subPath = block();
            if (subPath) {
                [self.innerSubPaths addObject:subPath];
            }
        }
    }
}

- (void)paintInLayer:(CALayer*)superLayer {
    if (superLayer) {
        if (![self.superlayer isEqual:superLayer]) {
            [superLayer addSublayer:self];
        }
        [self setNeedsDisplay];
    }
}

- (HACPathPainter*)clearContents {
    if (self.generatePathAsynchronously) {
        dispatch_async([HACPainterDisplay serialOperationQueue], ^{
            [self.innerSubPaths removeAllObjects];
        });
    } else {
        [self.innerSubPaths removeAllObjects];
    }
    return self;
}

- (NSMutableArray<UIBezierPath *> *)innerSubPaths {
    if (!_innerSubPaths) {
        _innerSubPaths = [NSMutableArray array];
    }
    return _innerSubPaths;
}
@end

@implementation HACTextPainter
+ (instancetype)painterLayer {
    HACPainterDisplay *paintDisplay = [[HACPainterDisplay alloc] init];
    HACTextPainter *instance = [[HACTextPainter alloc] initWithDelegate:paintDisplay];
    objc_setAssociatedObject(instance, HACPainterDefaultDelegateKey, paintDisplay, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    return instance;
}

- (instancetype)initWithDelegate:(id<HACPainterDelegate>)delegate {
    if (self = [self init]) {
        self.painterDelegate = delegate;
        self.displaysAsynchronously = YES;
    }
    return self;
}

- (void)setPainterDelegate:(id<HACPainterDelegate>)painterDelegate {
    _painterDelegate = painterDelegate;
    // retain painter delegate.
    id<HACPainterDelegate> defaultDelegate = objc_getAssociatedObject(self, HACPainterDefaultDelegateKey);
    if (defaultDelegate) {
        objc_removeAssociatedObjects(defaultDelegate);
    }
}

- (void)display {
    if (self.painterDelegate) {
        [self.painterDelegate displayLayer:self asynchronously:self.displaysAsynchronously];
    } else {
        [super display];
    }
}

- (void)supperDisplay {
    [super display];
}

- (void)paintInLayer:(CALayer*)superLayer {
    if (superLayer) {
        if (![self.superlayer isEqual:superLayer]) {
            [superLayer addSublayer:self];
        }
        [self setNeedsDisplay];
    }
}
@end

@implementation HACGradientPainter
+ (instancetype)painterLayer {
    HACPainterDisplay *paintDisplay = [[HACPainterDisplay alloc] init];
    HACGradientPainter *instance = [[HACGradientPainter alloc] initWithDelegate:paintDisplay];
    objc_setAssociatedObject(instance, HACPainterDefaultDelegateKey, paintDisplay, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    return instance;
}

- (instancetype)initWithDelegate:(id<HACPainterDelegate>)delegate {
    if (self = [super init]) {
        self.painterDelegate = delegate;
        self.displaysAsynchronously = YES;
    }
    return self;
}

- (void)setPainterDelegate:(id<HACPainterDelegate>)painterDelegate {
    _painterDelegate = painterDelegate;
    // retain painter delegate.
    id<HACPainterDelegate> defaultDelegate = objc_getAssociatedObject(self, HACPainterDefaultDelegateKey);
    if (defaultDelegate) {
        objc_removeAssociatedObjects(defaultDelegate);
    }
}

- (void)display {
    if (self.painterDelegate) {
        [self.painterDelegate displayLayer:self asynchronously:self.displaysAsynchronously];
    } else {
        [super display];
    }
}

- (void)paintInLayer:(CALayer*)superLayer {
    if (superLayer) {
        if (![self.superlayer isEqual:superLayer]) {
            [superLayer addSublayer:self];
        }
        [self setNeedsDisplay];
    }
}

- (void)supperDisplay {
    [super display];
}
@end

@interface HACGradienPathPainter ()
@property (nonatomic, strong) HACGradientPainter *gradientPainter;
@property (nonatomic, strong) HACPathPainter *pathPainter;

@end

@implementation HACGradienPathPainter

- (instancetype)initWithDelegate:(id<HACPainterDelegate>)delegate {
    if (self = [self init]) {
        self.painterDelegate = delegate;
        self.displaysAsynchronously = YES;
    }
    return self;
}

- (HACGradientPainter *)gradientPainter {
    if (!_gradientPainter) {
        _gradientPainter = [HACGradientPainter painterLayer];
        _gradientPainter.locations         = @[@0,@0.5];
        _gradientPainter.startPoint        = CGPointMake(0, 1);
        _gradientPainter.endPoint          = CGPointMake(0, 0);
        _gradientPainter.anchorPoint = CGPointMake(0, 0);
    }
    return _gradientPainter;
}

- (HACPathPainter *)pathPainter {
    if (!_pathPainter) {
        _pathPainter = [HACPathPainter painterLayer];
        _pathPainter.lineWidth = 0.5;
        _pathPainter.anchorPoint = CGPointMake(0, 0);
    }
    return _pathPainter;
}

- (void)setColors:(NSArray *)colors {
    _colors = [colors copy];
    self.gradientPainter.colors = colors;
}

- (void)setPaintRect:(CGRect)paintRect {
    _paintRect = paintRect;
    self.gradientPainter.bounds = paintRect;
}

- (void)generatePath:(GeneratePathBlock)block {
    if (block) {
        [[self.pathPainter clearContents] generateSubPath:block];
    }
}

- (HACGradienPathPainter*)clearContents {
    [self.pathPainter clearContents];
    return self;
}

- (void)paintInLayer:(CALayer*)superLayer {
    if (superLayer) {
        if (![self.superlayer isEqual:superLayer]) {
            [superLayer addSublayer:self];
        }
        [self.gradientPainter paintInLayer:self];
        [self.pathPainter setNeedsDisplay];//事先画好再mask
        self.gradientPainter.mask = self.pathPainter;
        [self setNeedsDisplay];
    }
}
@end
